home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
IRIX 6.2 Applications 1996 May
/
SGI IRIX 6.2 Applications 1996 May.iso
/
dist
/
impr_dev.idb
/
usr
/
impressario
/
src
/
scan
/
template_driver
/
main.c.z
/
main.c
Wrap
C/C++ Source or Header
|
1996-05-06
|
39KB
|
1,614 lines
/*
* main.c
*
* main routine for the scanner drivers, scanner callback
* functions.
*
* This file should NOT be modified to support new scanners!!
*
* Copyright 1992, 1993, 1994 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <dslib.h>
#include <malloc.h>
#include <stdlib.h>
#include <invent.h>
#include <bstring.h>
#include <ulocks.h>
#include <string.h>
#include <locale.h>
#include <msgs/uxsgiimpr.h>
#include <scanner.h>
#include <scanipc.h>
#include <scandrv.h>
#include <scanconv.h>
#include "scan.h"
/*
* Since the units of resolution are in the denominator, going from
* inches to centimeters and back is the inverse of what it is when
* the units are in the numerator.
*/
#define RESINCHTOCM(inch) CMTOINCH(inch)
#define RESCMTOINCH(cm) INCHTOCM(cm)
#define ISRGBPIX8(t) ((t)->packing == SC_PACKPIX && \
(t)->channels == 3 && \
(t)->type == SC_RGB && \
(t)->bpp == 8)
#define ISRGBPIX16(t) ((t)->packing == SC_PACKPIX && \
(t)->channels == 3 && \
(t)->type == SC_RGB && \
(t)->bpp > 8)
#define ISRGBPIX1(t) ((t)->packing == SC_PACKPIX && \
(t)->channels == 3 && \
(t)->type == SC_RGB && \
(t)->bpp == 1)
#define ISRGBPLANE8(t) ((t)->packing == SC_PACKPLANE && \
(t)->channels == 3 && \
(t)->type == SC_RGB && \
(t)->bpp == 8)
#define ISRGBPLANE16(t) ((t)->packing == SC_PACKPLANE && \
(t)->channels == 3 && \
(t)->type == SC_RGB && \
(t)->bpp > 8)
#define ISGREY8(t) ((t)->packing == SC_PACKPIX && \
(t)->channels == 1 && \
(t)->type == SC_GREY && \
(t)->bpp == 8)
#define ISGREY16(t) ((t)->packing == SC_PACKPIX && \
(t)->channels == 1 && \
(t)->type == SC_GREY && \
(t)->bpp > 8)
#define ISMONO1(t) ((t)->packing == SC_PACKPIX && \
(t)->channels == 1 && \
(t)->type == SC_MONO && \
(t)->bpp == 1)
int drverr;
char *drvmsg;
extern int mpin(void *addr, unsigned len);
extern int munpin(void *addr, unsigned len);
static pid_t scanpid, imgpid; /* child process ids */
static SCANINFO *scan; /* info about the scanner */
static SCANPARAMS sparams; /* info about the scan to take place */
static char *scanbuf; /* memory to buffer scan lines */
static unsigned bytesPinned = 0; /* Number of bytes pinned into */
/* memory */
static long maxmem; /* max memory to allocat for */
/* scanbuf */
static long xpixels, xbytes, ysize; /* The size of the scan data we */
/* will be returning to the */
/* scanning application. The */
/* fields of sparams describes the */
/* size of the scan data that the */
/* scan module will be returning */
/* to us */
static char *ibuf = NULL; /* Image processing line buffer */
static int *zmap = NULL; /* Zoom map */
static SCSTATUS status; /* Current scan status */
static int kids = 0; /* Number of child processes */
/* running */
/*
* Lin conversion function for DoImgProc
*/
static void (*convert)(void *from, int fx, void *to, int tx, int *z);
/*
* static void
* ScanCleanup(void)
*
* Description:
* Free all the resources used by a scan, preparing things for a
* subsequent scanning operation.
*
* This should not be called from a signal handler (specifically,
* childdeath); use SCDriverSyncFunction. It should never be
* called if either of the child processes are still around.
*/
static void
ScanCleanup(void)
{
int statusPut = 0;
/*
* Get rid of the queues and free memory
*/
if (sparams.scanq) {
SCDestroyQueue(sparams.scanq);
sparams.scanq = NULL;
}
if (sparams.sfreeq) {
SCDestroyQueue(sparams.sfreeq);
sparams.sfreeq = NULL;
}
if (scanbuf) {
if (bytesPinned) {
if (munpin(scanbuf, bytesPinned) < 0) {
#ifdef DEBUG
perror("munpin");
#endif
}
bytesPinned = 0;
}
free(scanbuf);
scanbuf = NULL;
}
if (ibuf) {
free(ibuf);
ibuf = NULL;
}
if (zmap) {
SCDestroyZoomMap(zmap);
zmap = NULL;
}
if (status.state == SC_ERROR) {
SCDriverPutStatus(&status,
status.errno == SCEDRVMSG ? drvmsg : NULL);
statusPut = 1;
}
if (status.state != SC_READY) {
status.state = SC_READY;
status.curline = 0;
status.pass = 0;
if (!statusPut) {
SCDriverPutStatus(&status, NULL);
}
(void)ScanReset(scan);
}
}
/*
* static void
* EndScanning(void)
*
* Description:
* Tell the child processes that are doing the scanning and
* converting to exit. SCQueueSetExit causes processes that call
* SCEnqueue or SCDequeue on that queue to exit, and
* SCDriverStopWriter causes processes that call SCDriverPutRow
* to exit.
*/
static void
EndScanning(void)
{
if (sparams.scanq) {
SCQueueSetExit(sparams.scanq);
}
if (sparams.sfreeq) {
SCQueueSetExit(sparams.sfreeq);
}
SCDriverStopWriter(imgpid);
}
/*
* static void
* childdeath(int sig)
*
* Description:
* Either scanproc or imgproc exited. wait() to find the exit
* status; if exit status was non-zero, then drverr contains a
* suitable error code and we set the status indicate that an
* error occurred. If the process died due to a signal, we don't
* really know what happened so we'll call it a device error.
*
* Parameters:
* sig - signal that caused us to be called (SIGCLD)
*/
/*ARGSUSED*/
static void
childdeath(int sig)
{
pid_t pid;
int stat;
for (pid = waitpid(-1, &stat, WNOHANG);
pid > 0;
pid = waitpid(-1, &stat, WNOHANG)) {
kids--;
if (status.state == SC_SCANNING &&
(WIFEXITED(stat) && WEXITSTATUS(stat) != 0
|| WIFSIGNALED(stat))) {
if (WIFSIGNALED(stat)) {
drverr = SCEDEV;
}
/*
* Don't actually call SCDriverPutStatus here, because
* that can lead to deadlock. ScanCleanup should notice
* that an error has occurred and set status
* appropriately (but it doesn't right now).
*/
status.state = SC_ERROR;
status.errno = drverr;
/*
* Call EndScanning synchronously. This will have the
* effect of ending the other child process. When the
* other child process has exited, we'll end up in this
* signal handler again, kids will be 0, and we'll cause
* ScanCleanup to be called.
*/
SCDriverSyncFunction((void (*)(void *))EndScanning, NULL);
}
/*
* Once they're both dead, do the cleanup
*/
if (kids == 0) {
SCDriverSyncFunction((void (*)(void *))ScanCleanup, NULL);
}
}
}
/*
* void
* ExitCleanup(void)
*
* Description:
* Abort a scan if one is in progress, since we're about to exit.
* We can't just call AbortScan, because it relies on processing
* of SIGCHLD and SCDriverSyncFunction to call ScanCleanup.
* We'll never end up back in SCDriverMainLoop() after this, so
* we have to take care of everything inline.
*/
void
ExitCleanup(void)
{
sigset_t mask, omask;
int stat;
if (status.state == SC_SCANNING) {
(void)sigemptyset(&mask);
(void)sigaddset(&mask, SIGCHLD);
(void)sigprocmask(SIG_BLOCK, &mask, &omask);
EndScanning();
(void)waitpid(scanpid, &stat, 0);
(void)waitpid(imgpid, &stat, 0);
(void)ScanCleanup();
(void)sigprocmask(SIG_SETMASK, &omask, 0);
}
}
/*
* static void
* hangup(int sig)
*
* Description:
* SIGHUP handler. If our parent has exited, we should exit too.
*
* Parameters:
* sig SIGHUP
*/
/*ARGSUSED*/
static void
hangup(int sig)
{
if (getppid() == 1) {
ExitCleanup();
exit(0);
}
}
/*
* static void
* SetMaxMem(void)
*
* Description:
* Set maxmem, the upper limit on how much memory we'll malloc to
* do scanning.
*/
static void
SetMaxMem(void)
{
inventory_t *inv;
if (setinvent() == 0) {
while ((inv = getinvent()) != NULL) {
if (inv->inv_class == INV_MEMORY && inv->inv_type == INV_MAIN) {
/*
* Don't use more than 1/3 of main memory
*/
maxmem = inv->inv_state / 3;
endinvent();
return;
}
}
endinvent();
}
/*
* Default to 2 meg
*/
maxmem = 2 * 1024 * 1024;
}
/*
* void
* scanfunc(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* What follows are many similar functions that implement the
* server side of the scanning protocol. These commands are
* called from within SCDriverMainLoop(), which knows which
* function to call for a given command because we passed in
* scanTable. The order of the functions in scanTable is very
* important; the index in the table of each function must be the
* same as its SCN_ #define in <scanipc.h>.
*
* Each function gets called with its SCN_ #define as the first
* argument, the argument data as the second argument, and a
* result structure as the third argument. In general, each
* function casts the argument data (arg->data) to the
* appropriate type, and executes appropriately, making res->data
* point to a buffer containing the results.
*
* In the event of an error, the function should set res->errno
* to a value either from <errno.h> or from <scanner.h> and
* return. The library will report an error to the application
* if res->errno is non-zero.
*
* The library initializes all fields of the SCRES structure
* pointed to by res to 0 before calling each function.
*
* Parameters:
* cmd SCN_ #define for the command
* arg argument structure
* res results structure
*/
/*
* static void
* InitOK(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Make sure driver initialized ok
*/
/*ARGSUSED*/
static void
InitOK(int cmd, SCARG *arg, SCRES *res)
{
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
res->len = 0;
res->free = 0;
}
/*
* static void
* Die(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Obey the scanner application's order to die
*/
/*ARGSUSED*/
static void
Die(int cmd, SCARG *arg, SCRES *res)
{
ExitCleanup();
exit(0);
}
/*
* static void
* ResMinMax(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Inform the scanner application of the minimum and maximum
* values for the horizontal and vertical resolutions. Convert
* between metrics as necessary.
*
* Parameters:
* cmd SCN_MINMAXRES
* arg metric
* res SCMINMAXRES
*/
/*ARGSUSED*/
static void
ResMinMax(int cmd, SCARG *arg, SCRES *res)
{
static SCMINMAXRES r;
/*
* Test arg->data first so as not to break compatibility with
* applications built with old buggy library.
*/
int metric = arg->data ? *(int *)arg->data : SC_INCHES;
if (metric == scan->metric) {
r.maxx = scan->maxxres;
r.maxy = scan->maxyres;
} else if (metric == SC_INCHES) {
r.maxx = RESCMTOINCH(scan->maxxres);
r.maxy = RESCMTOINCH(scan->maxyres);
} else if (metric == SC_CENTIM) {
r.maxx = RESINCHTOCM(scan->maxxres);
r.maxy = RESINCHTOCM(scan->maxyres);
} else {
res->errno = SCEBADMETRIC;
return;
}
/*
* Always set the min res to 1; We'll zoom down from some hardware
* supported resolution to this.
*/
r.minx = 1;
r.miny = 1;
res->data = &r;
res->len = sizeof(r);
}
/*
* static void
* NumRes(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Inform the scanner application of the number of hardware
* resolutions supported. If this number is 0, we're telling the
* application that ALL resolutions are supported in hardware, or
* that the scanner itself is capable of doing very high quality
* rescaling of the image data as it scans it.
*
* Parameters:
* cmd SCN_NRES
* arg void
* res pointer to int
*/
/*ARGSUSED*/
static void
NumRes(int cmd, SCARG *arg, SCRES *res)
{
res->data = &scan->nres;
res->len = sizeof(scan->nres);
}
/*
* static void
* HardwareRes(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Inform scanner application of the scanning resolutions
* supported by the scanner itself, without having us do
* decimation or replication. Convert metrics as appropriate.
*
* Parameters:
* cmd SCN_RES
* arg metric
* res array of float
*/
/*ARGSUSED*/
static void
HardwareRes(int cmd, SCARG *arg, SCRES *res)
{
static float *r;
int metric = *(int *)arg->data;
int i;
if (scan->nres) {
if (r) {
free(r);
r = 0;
}
r = calloc(scan->nres * 2, sizeof(*r));
for (i = 0; i < scan->nres; i++) {
if (metric == scan->metric) {
r[i] = scan->xres[i];
r[i + scan->nres] = scan->yres[i];
} else if (metric == SC_INCHES) {
r[i] = RESCMTOINCH(scan->xres[i]);
r[i + scan->nres] = RESCMTOINCH(scan->yres[i]);
} else if (metric == SC_CENTIM) {
r[i] = RESINCHTOCM(scan->xres[i]);
r[i + scan->nres] = RESINCHTOCM(scan->yres[i]);
} else {
res->errno = SCEBADMETRIC;
return;
}
}
}
res->len = sizeof(*r) * scan->nres * 2;
res->data = r;
}
/*
* static void
* NumTypes(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Return to scanner application the number of types supported by
* this driver/scanner.
*
* Parameters:
* cmd SCN_NTYPES
* arg void
* res int
*/
/*ARGSUSED*/
static void
NumTypes(int cmd, SCARG *arg, SCRES *res)
{
res->data = &scan->ntypes;
res->len = sizeof(scan->ntypes);
return;
}
/*
* static void
* Types(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Tell scanner application what types we support
*
* Parameters:
* cmd SCN_TYPES
* arg void
* res array of SCDATATYPE
*/
/*ARGSUSED*/
static void
Types(int cmd, SCARG *arg, SCRES *res)
{
res->data = scan->types;
res->len = scan->ntypes * sizeof(SCDATATYPE);
}
/*
* static void
* AbortScan(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Stop scanning. Induce the child processes to exit. Arrange
* to have ScanCleanup called.
*
* Parameters:
* cmd SCN_ABORT
* arg void
* res void
*/
/*ARGSUSED*/
static void
AbortScan(int cmd, SCARG *arg, SCRES *res)
{
sigset_t mask, omask;
int stat;
if (status.state != SC_SCANNING) {
res->errno = SCENOTSCANNING;
return;
}
/*
* Block SIGCHLD while we're waiting for the children to exit, so
* that SIGCHLD handler won't wait instead.
*/
(void)sigemptyset(&mask);
(void)sigaddset(&mask, SIGCHLD);
(void)sigprocmask(SIG_BLOCK, &mask, &omask);
EndScanning();
(void)waitpid(scanpid, &stat, 0);
(void)waitpid(imgpid, &stat, 0);
kids = 0;
/*
* Restore the old signal mask.
*/
(void)sigprocmask(SIG_SETMASK, &omask, NULL);
/*
* Call SCDriverSyncFunction instead of ScanCleanup so that the
* app doesn't have to wait for us to do this unless it tries to
* issue another command
*/
SCDriverSyncFunction((void (*)(void *))ScanCleanup, NULL);
}
/*
* Forward declaration for StartScan
*/
static void
DoImgProc(SCANPARAMS *params);
/*
* static void
* StartScan(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Start scanning. Allocate scan buffers and queues, sproc the
* children to do the actual work.
*
* Parameters:
* cmd SCN_SCAN
* arg void
* res void
*/
static void
StartScan(int cmd, SCARG *arg, SCRES *res)
{
long scanlinebytes, qlines;
char *buf;
sigset_t mask, omask;
if (status.state != SC_READY) {
res->errno = status.state == SC_SCANNING ? SCEBUSY : drverr;
if (res->errno == SCEDRVMSG) {
res->errMsg = drvmsg;
}
return;
}
/*
* Allocate memory for the scan queue
*
* Make sure that each line is aligned on a 4 byte boundary. This
* is (so far) the lowest common denominator for allowing this
* code to work for all encountered scanners; scsi scanners
* require that buffers passed to the read routine are aligned on
* 4 byte boundaries, and the screen driver needs this as well for
* calling readdisplay().
*/
scanlinebytes = (sparams.xbytes + 3) & ~0x3;
if (!sparams.readlines) {
sparams.readlines =
MIN(sparams.maxmem / (scanlinebytes * 2), sparams.ylines);
if (!sparams.readlines) {
sparams.readlines = 1;
}
}
/*
* If readlines > ylines, no buffers will be put on the freeq in
* the code below, which will prevent any scanning at all from
* occurring.
*/
if (sparams.readlines > sparams.ylines) {
sparams.readlines = sparams.ylines;
}
qlines = MIN(sparams.readlines * 2, sparams.ylines);
bytesPinned = qlines * scanlinebytes;
scanbuf = malloc(bytesPinned);
if (mpin(scanbuf, bytesPinned) < 0) {
#ifdef DEBUG
perror("mpin");
#endif
bytesPinned = 0;
}
sparams.scanq = SCCreateQueue(qlines);
sparams.sfreeq = SCCreateQueue(qlines / sparams.readlines);
/*
* Chop the chunk we malloc'd into scanlinebyte-sized chunk *
* readlines sized chunks.
*/
buf = scanbuf;
for (buf = scanbuf;
qlines >= sparams.readlines;
qlines -= sparams.readlines) {
SCEnqueue(sparams.sfreeq, buf);
buf += scanlinebytes * sparams.readlines;
}
/*
* Set things up for DoImgProc
*
* SetupScan is supposed to set sparams.convert if any conversion
* is necessary to achieve one of the four basic types. The
* standard conversion routines all do zooming if necessary, so we
* don't have to worry about choosing an appropriate zoom function
* if we're already converting.
*/
convert = sparams.convert;
if (xpixels != sparams.xpixels) {
if (!convert) {
switch (sparams.type.type) {
case SC_RGB:
convert = sparams.type.packing == SC_PACKPIX ?
SCZoomRow24 : SCZoomRow8;
break;
case SC_GREY:
convert = SCZoomRow8;
break;
case SC_MONO:
convert = SCZoomRow1;
break;
default:
drverr = SCEBADTYPE;
exit(1);
}
}
zmap = SCCreateZoomMap(sparams.xpixels, xpixels);
}
if (convert) {
ibuf = malloc(xbytes);
}
/*
* Set status BEFORE creating children. If we call
* SCDriverPutStatus when we have children, deadlock can occur.
*/
status.state = SC_SCANNING;
status.curline = 0;
SCDriverPutStatus(&status, NULL);
/*
* We must block SIGCHLD while we're creating child processes,
* because our SIGCHLD signal handler uses the variable kids to
* determine whether or not scanning is going on. It's possible
* that one of our sproc children could exit before we got to the
* code that incremented kids, so that the count seen by the
* signal handler would be wrong.
*
* Yes, this actually happened.
*/
(void)sigemptyset(&mask);
(void)sigaddset(&mask, SIGCHLD);
(void)sigprocmask(SIG_BLOCK, &mask, &omask);
/*
* Set up child processes to do the scanning and zooming. DoScan
* gets buffers from sfreeq, scans data into them, and then puts
* them on scanq. DoImgProc takes buffers from scanq, zooms and
* converts them, and dispatches them with SCDriverPutRow.
*/
imgpid = sproc((void (*)(void *))DoImgProc, PR_SADDR,
(unsigned)&sparams);
if (imgpid < 0) {
(void)sigprocmask(SIG_SETMASK, &omask, 0);
AbortScan(cmd, arg, res);
res->errno = errno;
return;
}
kids++;
scanpid = sproc((void (*)(void *))DoScan, PR_SADDR,
(unsigned)&sparams);
if (scanpid < 0) {
(void)sigprocmask(SIG_SETMASK, &omask, 0);
AbortScan(cmd, arg, res);
res->errno = errno;
return;
}
kids++;
(void)sigprocmask(SIG_SETMASK, &omask, 0);
}
/*
* static void
* Setup(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Set up the scanner with the scan window and resolution in
* anticipation of a pending scan. Perform all necessary metric
* conversions.
*
* Parameters:
* cmd SCN_SETUP
* arg SCSETUP
* res void
*/
/*ARGSUSED*/
static void
Setup(int cmd, SCARG *arg, SCRES *res)
{
SCSETUP *s;
float xres, yres;
int i;
s = arg->data;
bzero(&sparams, sizeof sparams);
sparams.s = scan;
sparams.preview = s->preview;
if (scan->metric != s->rmetric) {
if (scan->metric == SC_INCHES) {
xres = RESCMTOINCH(s->xres);
yres = RESCMTOINCH(s->yres);
} else {
xres = RESINCHTOCM(s->xres);
yres = RESINCHTOCM(s->yres);
}
} else {
xres = s->xres;
yres = s->yres;
}
/*
* Set the sparams.xres and sparams.yres to the next highest
* resolution that the scanner actually supports, or the highest
* resolution if the scanner doesn't support resolutions higher
* than those requested.
*/
if (scan->nres && !scan->canZoom) {
for (i = 0; i < scan->nres; i++) {
if (scan->xres[i] >= xres || i == scan->nres - 1) {
sparams.xres = scan->xres[i];
break;
}
}
for (i = 0; i < scan->nres; i++) {
if (scan->yres[i] >= yres || i == scan->nres -1) {
sparams.yres = scan->yres[i];
break;
}
}
} else {
sparams.xres = ilimit(xres, scan->minxres, scan->maxxres);
sparams.yres = ilimit(yres, scan->minyres, scan->maxyres);
}
switch (s->wmetric) {
case SC_PIXELS:
sparams.x = s->x / xres;
sparams.y = s->y / yres;
sparams.width = s->width / xres;
sparams.height = s->height / yres;
xpixels = s->width;
ysize = s->height;
break;
case SC_INCHES:
if (scan->metric == SC_INCHES) {
sparams.x = s->x;
sparams.y = s->y;
sparams.width = s->width;
sparams.height = s->height;
} else {
sparams.x = INCHTOCM(s->x);
sparams.y = INCHTOCM(s->y);
sparams.width = INCHTOCM(s->width);
sparams.height = INCHTOCM(s->height);
}
xpixels = s->width * (s->rmetric == SC_INCHES ? s->xres :
RESCMTOINCH(s->xres));
ysize = s->height * (s->rmetric == SC_INCHES ? yres :
RESCMTOINCH(s->yres));
break;
case SC_CENTIM:
if (scan->metric == SC_CENTIM) {
sparams.x = s->x;
sparams.y = s->y;
sparams.width = s->width;
sparams.height = s->height;
} else {
sparams.x = CMTOINCH(s->x);
sparams.y = CMTOINCH(s->y);
sparams.width = CMTOINCH(s->width);
sparams.height = CMTOINCH(s->height);
}
xpixels = s->width * (s->rmetric == SC_CENTIM ? s->xres :
RESINCHTOCM(s->xres));
ysize = s->height * (s->rmetric == SC_CENTIM ? yres :
RESINCHTOCM(s->yres));
break;
default:
res->errno = SCENOTSUPPORTED;
return;
}
sparams.type = s->type;
sparams.maxmem = maxmem;
if (SetupScan(&sparams) < 0) {
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
return;
}
/*
* Don't do any zooming if we don't have to. Our calculations
* above may have yielded slightly different results than similar
* calculations in the scanner, and unless we were asked to return
* a specific number of pixels, we figure that image quality is
* the over-riding concern.
*/
if (xres == sparams.xres && yres == sparams.yres && s->wmetric !=
SC_PIXELS) {
xpixels = sparams.xpixels;
ysize = sparams.ylines;
}
/*
* Return an error if this scan would be a no-op.
*/
if (sparams.xpixels == 0 || sparams.ylines == 0 || xpixels == 0 ||
ysize == 0) {
res->errno = SCEAREATOOSMALL;
return;
}
if (ISRGBPIX8(&s->type)) {
xbytes = xpixels * 3;
} else if (ISRGBPIX16(&s->type)) {
xbytes = xpixels * 6;
} else if (ISRGBPIX1(&s->type)) {
xbytes = (xpixels + 1) / 2;
} else if (ISRGBPLANE8(&s->type) || ISGREY8(&s->type)) {
xbytes = xpixels;
} else if (ISRGBPLANE16(&s->type) || ISGREY16(&s->type)) {
xbytes = xpixels * 2;
} else if (ISMONO1(&s->type)) {
xbytes = (xpixels + 7) / 8;
} else {
res->errno = SCEBADTYPE;
return;
}
}
/*
* static void
* GetSize(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Return to the scanning application the size of the current
* scan, that set by the last call to Setup.
*
* Parameters:
* cmd SCN_GETSIZE
* arg void
* res SCSIZE
*/
/*ARGSUSED*/
static void
GetSize(int cmd, SCARG *arg, SCRES *res)
{
static SCSIZE sz;
sz.xbytes = xbytes;
sz.xpixels = xpixels;
sz.ysize = ysize;
res->data = &sz;
res->len = sizeof sz;
}
/*
* static void
* PageSize(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Inform the scanning application of the (x,y,width,height)
* coordinates of the supported scanning area, converting between
* metrics as necessary
*
* Parameters:
* cmd SCN_PAGESIZE
* arg metric
* res SCWINDOW
*/
/*ARGSUSED*/
static void
PageSize(int cmd, SCARG *arg, SCRES *res)
{
static SCWINDOW w = { 0, 0, 0, 0};
int metric = *(int *)arg->data;
if (metric != SC_INCHES && metric != SC_CENTIM) {
res->errno = SCEBADMETRIC;
return;
}
if (metric == scan->metric) {
w.x = scan->pagex;
w.y = scan->pagey;
w.width = scan->pagewidth;
w.height = scan->pageheight;
} else {
if (metric == SC_INCHES) {
w.x = CMTOINCH(scan->pagex);
w.y = CMTOINCH(scan->pagey);
w.width = CMTOINCH(scan->pagewidth);
w.height = CMTOINCH(scan->pageheight);
} else {
w.x = INCHTOCM(scan->pagex);
w.y = INCHTOCM(scan->pagey);
w.width = INCHTOCM(scan->pagewidth);
w.height = INCHTOCM(scan->pageheight);
}
}
res->data = &w;
res->len = sizeof w;
}
/*
* void
* FeederGetFlags(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Get this scanner's feeder flags for the scanner application
*
* Parameters:
* cmd SCN_FEEDERGETFLAGS
* arg none
* res SCFEEDERFLAGS *
*/
/*ARGSUSED*/
void
FeederGetFlags(int cmd, SCARG *arg, SCRES *res)
{
res->data = &scan->feederFlags;
res->len = sizeof scan->feederFlags;
}
/*
* void
* FeederSetFlags(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Set the feeder flags passed by the scanner application. This
* is used to select automatic or programmatic feeding for
* scanners that support both.
*
* Parameters:
* cmd SCN_FEEDERSETFLAGS
* arg SCFEEDERFLAGS *
* res none
*/
/*ARGSUSED*/
void
FeederSetFlags(int cmd, SCARG *arg, SCRES *res)
{
if (!(scan->feederFlags & SC_HASFEEDER)) {
res->errno = SCENOFEEDER;
return;
}
if (SetFeederFlags(scan, *(SCFEEDERFLAGS *)arg->data) < 0) {
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
}
}
/*
* void
* FeederAdvance(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Advance the feeder to the next document
*
* Parameters:
* cmd SCN_FEEDERADVANCE
* arg none
* res none
*/
/*ARGSUSED*/
void
FeederAdvance(int cmd, SCARG *arg, SCRES *res)
{
if (!(scan->feederFlags & SC_HASFEEDER)) {
res->errno = SCENOFEEDER;
return;
}
if (AdvanceFeeder(scan) < 0) {
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
}
}
/*
* void
* IsFeederReady(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Find out whether the document feeder is ready to be advanced;
* that is, whether a call to FeederAdvance would be successful.
*
* Parameters:
* cmd SCN_FEEDERREADY
* arg none
* res none
*/
/*ARGSUSED*/
void
IsFeederReady(int cmd, SCARG *arg, SCRES *res)
{
if (!(scan->feederFlags & SC_HASFEEDER)) {
res->errno = SCENOFEEDER;
return;
}
if (FeederReady(scan) < 0) {
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
}
}
/*
* void
* GetVersion(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Tell the scanner app what version of libscan we linked with.
*
* Parameters:
* cmd SCN_GETVERSION
* arg none
* res float *
*/
/*ARGSUSED*/
void
GetVersion(int cmd, SCARG *arg, SCRES *res)
{
static float version = SC_VERSION;
res->data = &version;
res->len = sizeof version;
}
/*
* void
* GetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Get options to save from the scanner. This function is used
* for two different IPCs: SCN_GETSAVEOPTLEN and SCN_GETSAVEOPT.
* We store the return value for SCN_GETSAVEOPT when
* SCN_GETSAVEOPTLEN is called, so in case anything happens that
* changes the size of the saveable options between these two
* IPC's we don't end up writing inconsistent data back to the
* app (although it will be stale).
*
* In practice these two commands should come one after the
* other, so there shouldn't be any problem with storing the data
* for a very short period of time.
*
* This also makes the interface with the scan.c portion of this
* command very simple, which is very desirable.
*
* Parameters:
* cmd SCN_GETSAVEOPTLEN or SCN_GETSAVEOPT
* arg NULL
* res int * or void *
*/
void
GetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
{
static int nBytes;
static void *optBuf = NULL, *options;
if (cmd == SCN_GETSAVEOPTLEN) {
options = GetSaveOptions(scan, &nBytes);
if (!options) {
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
return;
}
if (optBuf) {
free(optBuf);
}
optBuf = malloc(nBytes);
bcopy(options, optBuf, nBytes);
res->data = &nBytes;
res->len = sizeof nBytes;
} else {
/*
* This is actually a protocol error; the client libscan
* should have called SCN_GETSAVEOPTLEN before calling
* SCN_GETSAVEOPT
*/
if (!options) {
res->errno = SCENOSAVEOPT;
return;
}
res->data = optBuf;
res->len = nBytes;
}
}
/*
* void
* SetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
*
* Description:
* Set options based on saved values from the scanner application
*
* Parameters:
* cmd SCN_SETSAVEOPT
* arg void *
* res NULL
*/
void
SetSaveOptionsCB(int cmd, SCARG *arg, SCRES *res)
{
if (SetSaveOptions(scan, arg->data, arg->len) == -1) {
res->errno = drverr;
if (drverr == SCEDRVMSG) {
res->errMsg = drvmsg;
}
}
}
/*
* Table of functions to be passed to the scan library. Order is of
* the utmost importance here; the position of each function in this
* table corresponds to the SCN_ #define in <scanipc.h> of the
* command that it implements.
*/
SCANFUNC scanTable[] = {
InitOK,
Die,
ResMinMax,
NumRes,
HardwareRes,
NumTypes,
Types,
PageSize,
Setup,
StartScan,
AbortScan,
GetSize,
FeederGetFlags,
FeederSetFlags,
FeederAdvance,
IsFeederReady,
GetVersion,
/*
* GetSaveOptionsCB is here twice because it's the callback for
* two separate commands.
*/
GetSaveOptionsCB,
GetSaveOptionsCB,
SetSaveOptionsCB
};
/*
* static void
* usage(char *pn)
*
* Description:
* Print a usage line for a scanner driver
*
* Parameters:
* pn program name to print usage for
*/
static void
usage(char *pn)
{
(void)fprintf(stderr, "usage: %s -query\n", pn);
(void)fprintf(stderr, " %s -version\n", pn);
(void)fprintf(stderr, " %s -install <device>\n", pn);
(void)fprintf(stderr, " %s -delete <device>\n", pn);
(void)fprintf(stderr, " %s <device> <arena>\n", pn);
}
/*
* int
* main(int argc, char *argv[])
*
* Description:
* main routine for the scanner driver. Should be called
* with argv[1] == name of the device which corresponds to the
* scanner to open and argv[2] == name of the us arena file used
* for communication with the scanning application.
*
* Parameters:
* argc - number of command line arguments
* argv - command line arguments
*
* Returns:
* exit status
*/
int
main(int argc, char *argv[])
{
struct sigaction act;
drverr = 0;
(void)setlocale(LC_ALL, "");
if (argc < 2) {
usage(argv[0]);
return 1;
}
if (strcmp(argv[1], "-query") == 0) {
PrintID(stdout);
FindScanners(stdout);
exit(0);
} else if (strcmp(argv[1], "-version") == 0) {
/*
* Don't change this. The install tool may use this
* information in future versions of Impressario if the
* communications protocol between clients and applications
* change.
*/
(void)printf("%g\n", SC_VERSION);
exit(0);
} else if (strcmp(argv[1], "-install") == 0) {
if (argc != 3) {
usage(argv[0]);
return 1;
}
return InstallScanner(argv[2]) == 0 ? 0 : 1;
} else if (strcmp(argv[1], "-delete") == 0) {
if (argc != 3) {
usage(argv[0]);
return 1;
}
return DeleteScanner(argv[2]) == 0 ? 0 : 1;
}
if (argc != 3) {
usage(argv[0]);
return 1;
}
if (SCDriverInit(argv[2]) < 0) {
drverr = errno;
} else {
scan = OpenScanner(argv[1]);
SetMaxMem();
act.sa_handler = childdeath;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGCHLD);
act.sa_flags = 0;
(void)sigaction(SIGCHLD, &act, NULL);
status.state = SC_READY;
status.curline = 0;
status.pass = 0;
SCDriverPutStatus(&status, NULL);
}
/*
* Properly handle SIGHUP, which will get sent to us if our
* parent, which is the scanning application, exits.
*/
act.sa_handler = hangup;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
(void)sigaction(SIGHUP, &act, NULL);
(void)sigaction(SIGQUIT, &act, NULL);
/*
* Call setsid, so we don't get signals propogated to us if our
* parent dies. When that happens, we will get a SIGHUP, and
* we'll clean up from there.
*/
setsid();
SCDriverSetCallbacks(scanTable, sizeof(scanTable)/sizeof(*scanTable),
scan ? scan->options : NULL, scan ?
scan->noptions : 0);
SCDriverMainLoop();
return 0;
}
/*
* static void
* DoImgProc(SCANPARAMS *params)
*
* Description:
* Take the scanned data off of params->scanq, scale and convert
* it to specification, and call SCDriverPutRow() with the
* result.
*
* This is meant to be passed to sproc so that it runs as a child
* thread of the main driver thread. It exits when it's done,
* with a status of 0 if everything was OK and a status of 1 if
* something went wrong. When exiting with a status of 1, we set
* drverr to indicate what the problem was.
*
* WARNING:
* Calls to SCEnqueue, SCDequeue, and SCDriverPutRow might not
* return; ie these functions might call exit. If you modify
* this code, be careful that you don't strand any resources due
* to one of these three functions exiting.
*
* Parameters:
* params parameters describing the scan
*/
static void
DoImgProc(SCANPARAMS *params)
{
int imgy, scany, cury;
float fy;
char *buf;
char *tofree = 0;
int npasses;
int scanChunk;
SCDriverInitChild();
(void)prctl(PR_TERMCHILD);
/*
* scanChunk is the number of bytes in a chunk that sits on the
* scanq. The scanning thread will typically scan many lines at a
* time for efficiency, and break up the buffer into line
* components for us to digest. It's then our job to put the
* lines back together for the scanning thread.
*/
scanChunk = ((params->xbytes + 3) & ~3) * params->readlines;
cury = -1;
imgy = 0;
npasses = sparams.type.type == SC_RGB && sparams.type.packing ==
SC_PACKPLANE ? 3 : 1;
while (npasses--) {
while (imgy < ysize) {
/*
* Zoom in the vertical direction; this involves either
* repeating rows or skipping rows. We use GRIDTOFLOAT and
* FLOATTOGRID to figure out which row we want this iteration,
* and then skip rows until we get the right one.
*/
fy = GRIDTOFLOAT(imgy, ysize);
scany = FLOATTOGRID(fy, params->ylines);
while(cury < scany) {
cury++;
if (cury % params->readlines == 0 && tofree) {
SCEnqueue(params->sfreeq, tofree);
tofree = 0;
}
buf = SCDequeue(params->scanq);
if (((int)buf - (int)scanbuf) % scanChunk == 0) {
/*
* This should never happen. As long as tofree
* occurs somewhere in each readlines lines, and
* the tofree from one group of lines doesn't end
* up in another group, we won't get here.
*/
if (tofree) {
SCEnqueue(params->sfreeq, tofree);
tofree = 0;
}
tofree = buf;
}
}
if (convert) {
convert(buf, params->xpixels, ibuf, xpixels, zmap);
}
if (SCDriverPutRow(convert ? ibuf : buf, xbytes) < 0) {
drverr = errno;
exit(1);
}
imgy = imgy + 1 % ysize;
status.curline = imgy;
status.pass = imgy / ysize;
SCDriverPutStatus(&status, NULL);
}
/*
* Grab any lines at the end that we're skipping. We need to
* do this so that the scanning thread doesn't get wedged
* trying to enqueue scanned buffers, and also to clear things
* out for multi-pass scanners.
*/
while (cury++ < params->ylines - 1) {
if (cury % params->readlines == 0 && tofree) {
SCEnqueue(params->sfreeq, tofree);
tofree = 0;
}
buf = SCDequeue(params->scanq);
if (((int)buf - (int)scanbuf) % scanChunk == 0) {
/*
* This should never happen. As long as tofree
* occurs somewhere in each readlines lines, and
* the tofree from one group of lines doesn't end
* up in another group, we won't get here.
*/
if (tofree) {
SCEnqueue(params->sfreeq, tofree);
tofree = 0;
}
tofree = buf;
}
}
cury = -1;
imgy = 0;
}
/*
* Not strictly necessary (because ScanCleanup destroys the queue
* and frees the memory), but still good form.
*/
if (tofree) {
SCEnqueue(params->sfreeq, tofree);
tofree = 0;
}
exit(0);
}